#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="HorizontalBlur.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010, 2011
//       Roland Rosenkranz (Glatzemann@email.de)
//
//    Based on libnoise by Jason Bevins
//      Copyright (C) 2003, 2004 Jason Bevins (licensed under LGPL)
// </copyright>
// <license>
//   This file is part of starLiGHT.Noise.
//
//   starLiGHT.Noise is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Noise is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Noise. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Noise are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 10385                 $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-06-14 07:59:17 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.Noise.Noise
{
    // http://home.scarlet.be/zoetrope/blur.htm

    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;

    #endregion

    public class HorizontalBlur : Module
    {
        private const int DEFAULT_BLUR_WIDTH = 8;
        private const double DEFAULT_E = 1.5;
        private const double DEFAULT_AMPLIFY = 1.0;

        private List<Parameter> parameterList;
        private int blurWidth = DEFAULT_BLUR_WIDTH;
        private int left = 0;
        private int right = 255;
        private int top = 0;
        private int bottom = 255;
        private double e = DEFAULT_E;
        private double amplify = DEFAULT_AMPLIFY;
        private double[] weights;

        public HorizontalBlur() : base(1)
        {
            cacheSource[0] = true;
        }

        public override List<Parameter> ParameterList
        {
            get
            {
                if (this.parameterList == null)
                {
                    this.parameterList = new List<Parameter>();
                    this.parameterList.Add(new Parameter("Width", "int", DEFAULT_BLUR_WIDTH, 1, 255));
                    this.parameterList.Add(new Parameter("this.E", "double", DEFAULT_E, 0.0, 128.0));
                    this.parameterList.Add(new Parameter("Amplify", "double", DEFAULT_AMPLIFY, 0.0, 128.0));
                }

                return this.parameterList;
            }
        }

        public override double GetValue(double x, double y, double z, double lowerXBound, double upperXBound, double lowerZBound, double upperZBound)
        {
            System.Diagnostics.Debug.Assert(sourceModule[0] != null, String.Empty);

            // Horizontal blur

            this.calculateWeights();

            double width = upperXBound - lowerXBound;
            double height = upperZBound - lowerZBound;
            int coordX = (int)Math.Floor(noisegen.MakeInt32Range(((double)(this.right - this.left)) / width * (x - lowerXBound)));
            int coordY = (int)Math.Floor(noisegen.MakeInt32Range(((double)(this.bottom - this.top)) / height * (z - lowerZBound)));
            double weightsum = 0.0;
            double value = 0.0;

            double stepX = width / (double)(this.right - this.left);

            int stepIdx = 0;
            for (double i = x - (this.blurWidth * stepX); i < x + (this.blurWidth * stepX); i += stepX)
            {
                value += GetValueInternal(0, i, y, z, lowerXBound, upperXBound, lowerZBound, upperZBound) * this.weights[stepIdx];
                weightsum += this.weights[stepIdx];
                stepIdx++;
            }

            value /= weightsum;

            return value * this.amplify;
        }

        public override void SetParameter(string name, object value)
        {
            switch (name.ToLower())
            {
                case "width":
                    this.blurWidth = Convert.ToInt32(value);
                    this.weights = null;
                    break;
                case "e":
                    this.e = Convert.ToDouble(value);
                    this.weights = null;
                    break;
                case "amplify":
                    this.amplify = Convert.ToDouble(value);
                    this.weights = null;
                    break;
            }
        }

        public override object GetParameter(string name)
        {
            switch (name.ToLower())
            {
                case "width": return this.blurWidth;
                case "e": return this.e;
                case "amplify": return this.amplify;
            }

            return null;
        }

        private double Gauss(double x, double middle, double width)
        {
            if (this.blurWidth <= 0) return 1.0;

            double t = -(1.0 / this.blurWidth) * ((middle - x) * (middle - x));
            return Math.Pow(this.e, t);
        }

        private void calculateWeights()
        {
            if (this.weights == null)
            {
                this.weights = new double[this.blurWidth * 2 + 1];

                for (int i = 0; i < this.blurWidth * 2 + 1; i++)
                {
                    double yG = this.Gauss(-this.blurWidth + i, 0, this.blurWidth);
                    this.weights[i] = yG;
                }
            }
        }
    }
}

